<script setup>
import { ElMessage } from 'element-plus'
import { getMentionList, parseMention, textHandle } from '@/utils/dialog-utils'
import { Connection } from '@element-plus/icons-vue'
import emojiData from '@/components/emoji/emoji-data'
import '@/components/emoji/style.css'
import useChannel from '@/store/channel'
import useUser from '@/store/user.js'
import useEventBus from '@/use/use-event-bus.js'
import useApp from '@/store/app'

const emit = defineEmits(['send', 'fileAdd', 'filesChange'])

const props = defineProps({
  disabledTool: Boolean,
})

const channelStore = useChannel()
const userStore = useUser()
const appStore = useApp()
const eventBus = useEventBus()

// 文本域
const textareaRef = ref()
// 文本展示
const textViewRef = ref()
// 待上传的文件
const files = ref([])

const emojiRef = ref()
const menuVisible = ref(false)
const menuPosition = ref({
  left: 0,
  top: 0,
})
// 引用的消息
const quoteMsg = ref(null)
// 消息
const messageBox = ref({
  content: '',
  // @用户列表
  mentionUserList: [],
  // 优先级
  priority: 0,
  loading: false,
})
// 文本展示大小
const textViewSize = useElementSize(textViewRef)
// 更多操作展示
const operationShow = ref(false)
// 更多操作组件
const operationComponent = ref('')
// 文本域行数
const rows = computed(() => {
  const autoWrap = Math.trunc(textViewSize.height.value / 22)
  const length = messageBox.value.content.split(/\n/).length
  const max = Math.max(autoWrap, length)
  return max > 5 ? 5 : max
})
// 文本内容
const textContent = computed(() => {
  return messageBox.value.content.split(/\n/)
})
const searchWord = ref('')

/**
 * 发送
 */
function send() {
  if (textEmpty()) {
    ElMessage({
      message: '消息不能为空!',
      type: 'warning',
    })
    return
  }
  emit('send', messageBox.value)
  clean()
}
// 是否禁言
const isProhibition = computed(() => {
  return (
    (channelStore.dialogExt?.banConfig?.banAllFlag &&
      !channelStore.isManager &&
      !channelStore.dialogExt.banConfig.whitelist.some((i) => i === userStore.userId)) ||
    channelStore.dialogExt?.banConfig?.blacklist.some((i) => i === userStore.userId)
  )
})
function textEmpty() {
  if (!messageBox.value.content) {
    // text无值,也没有文件
    if (!files.value.length) {
      return true
    }
  }
  return false
}

/**
 * 重置消息
 */
function clean() {
  files.value = []
  quoteMsg.value = null
  messageBox.value = {
    content: '',
    // @用户列表
    mentionUserList: [],
    // 优先级
    priority: 0,
    loading: false,
  }
}

/**
 * 同步输入框和展示框的高度
 * @param scrollTopVal
 */
function unifiedScrollTop(scrollTopVal = null) {
  setTimeout(() => {
    const scrollTop =
      scrollTopVal !== null ? scrollTopVal : textareaRef.value.scrollHeight - textareaRef.value.clientHeight
    textareaRef.value.scrollTop = scrollTop
    textViewRef.value.scrollTop = scrollTop
  }, 5)
}

/**
 * 文本域输入
 * 由于v-model会自动处理文本拼写,导致拼写时文本展示为空白,所以这里处理一下
 */
function textareaInput(e) {
  messageBox.value.content = e.target.value
  if (operationShow.value) {
    // 关键词只需要光标之前的字符
    const word = e.target.value.slice(0, textareaRef.value.selectionStart).match(/(?<=@)[^@]+(?!.)/)
    searchWord.value = word ? word[0] : ''
  }
  if (!messageBox.value.content) {
    operationComponent.value = null
  }
}

function clickEmoji(emoji) {
  emojiRef.value.closePop()
  textareaFocus().then(() => {
    interpolation(`[${emoji.type === 'default' ? '' : `${emoji.type}_`}${emoji.name}]`)
    if (emoji.type !== 'default') {
      send()
    }
  })
}

function removeMentionUser() {
  const mentionList = getMentionList(messageBox.value.content)
  if (!mentionList.length) {
    messageBox.value.mentionUserList = []
  } else {
    for (let i = 0; i < messageBox.value.mentionUserList.length; i++) {
      if (!mentionList[i] || mentionList[i] !== messageBox.value.mentionUserList[i]) {
        messageBox.value.mentionUserList.splice(i, 1)
        return
      }
    }
  }
}

/**
 * 指令类型的选择
 */
function select(info) {
  if (operationComponent.value === 'MentionUser') {
    interpolation(`${info.text} `)
    messageBox.value.mentionUserList.push(...info.list)
  }
  operationComponent.value = ''
  searchWord.value = ''
  textareaFocus()
}

/**
 * 文本域插值
 * @param {string} str 插入的字符
 * @param {number} index 插值位置
 */
function interpolation(str, index = null) {
  if (!str) return
  !index && (index = textareaRef.value.selectionStart)
  const beforeStr = messageBox.value.content.slice(0, index - searchWord.value.length)
  const afterStr = messageBox.value.content.slice(index)
  messageBox.value.content = beforeStr + str + afterStr
  const newPosition = index + str.length - searchWord.value.length
  setTextareaCursor(newPosition)
  textareaFocus()
}

/**
 * 设置文本域光标位置
 * @param {number} v
 */
function setTextareaCursor(v) {
  nextTick(() => {
    textareaRef.value.selectionStart = v
    textareaRef.value.selectionEnd = v
  })
}

function textareaFocus(focus = true) {
  return new Promise((resolve) => {
    setTimeout(() => {
      focus ? textareaRef.value?.focus() : textareaRef.value?.blur()
      resolve()
    }, 200)
  })
}

/**
 * 删除文件
 */
function deleteFile(file) {
  const index = files.value.findIndex((i) => i === file)
  files.value.splice(index, 1)
  emit('filesChange', files.value)
}

// 当设置操作组件时自动展开
watch(operationComponent, (v) => {
  operationShow.value = Boolean(v)
})

watch(
  () => messageBox.value.content,
  (newText, oldText) => {
    // 删除
    if (oldText.length > newText.length) {
      // 差异占位符
      let pl = 0
      const delText = oldText.split('').reduce((str, i, index) => {
        if (newText[index - pl] === undefined || i !== newText[index - pl]) {
          str += i
          pl++
        }
        return str
      }, '')
      // 删除 @、命令、任务 关闭操作面板
      if (['@'].includes(delText) && operationShow.value) {
        operationComponent.value = ''
        // 删除空格,如果是被@消息则删除整个@消息
      } else if (delText === ' ') {
        const mentionResult = parseMention(oldText, {
          reverse: true,
          cursorStart: textareaRef.value.selectionStart + 1,
        })
        if (mentionResult.has && !mentionResult.next) {
          messageBox.value.content = newText.replace(mentionResult.target, '')
          removeMentionUser()
          setTextareaCursor(textareaRef.value.selectionStart - mentionResult.target.length)
        }
      } else {
        if (operationShow.value) {
          searchWord.value = searchWord.value.replace(delText, '')
        }
      }
    }
  }
)

// 用户输入时自动获取焦点
onStartTyping(() => {
  if (!textareaRef.value.active) textareaRef.value.focus()
})

function closeMoreOperation() {
  operationComponent.value = ''
}

function handlePaste() {
  eventBus.emit('getCopyText', (text) => {
    interpolation(text)
  })
}

// 处理右键菜单
function handleContextmenu(e) {
  e.preventDefault()
  menuPosition.value = {
    left: e.offsetX + 'px',
    top: e.offsetY - 20 + 'px',
  }
  menuVisible.value = true
}

// 更改发送方式
function changeTheSendingMethod(e) {
  appStore.setSendShortcutKey(e)
}

// 工具事件
function handleToolClick(type) {
  if (type === '@') {
    interpolation('@')
    operationComponent.value = 'MentionUser'
  }
}

useEventListener('click', () => {
  menuVisible.value = false
})

onMounted(() => {
  useEventListener(textareaRef, 'paste', (e) => {
    const clipboardItems = e.clipboardData.items
    Array.from(clipboardItems).forEach((i) => {
      if (i.kind === 'file') {
        const blob = i.getAsFile()
        files.value.push(blob)
        emit('fileAdd', blob)
        emit('filesChange', files.value)
      } else if (i.kind === 'string') {
        unifiedScrollTop()
      }
    })
  })
  useEventListener(textareaRef, 'keydown', (e) => {
    const isEnter = e.code === 'Enter' || e.code === 'NumpadEnter'
    // 手动换行
    if ((e.ctrlKey || e.altKey) && isEnter && Number(appStore.sendShortcutKey) === 1) {
      interpolation('\n')
      unifiedScrollTop()
    } else if (!e.ctrlKey && isEnter && Number(appStore.sendShortcutKey) === 2) {
      if (e.altKey) {
        interpolation('\n')
      }
      unifiedScrollTop()
    } else if (!e.altKey && isEnter && Number(appStore.sendShortcutKey) === 3) {
      if (e.ctrlKey) {
        interpolation('\n')
      }
      unifiedScrollTop()
    } else if (!isEnter && !e.ctrlKey && !e.altKey && e.key !== 'Shift') {
      // 输入@
      if (e.shiftKey && e.code === 'Digit2') {
        operationComponent.value = 'MentionUser'
      }
      // 操作面板打开时
      if (operationShow.value) {
        // 禁止文本域光标上下移动
        if (e.code === 'ArrowUp' || e.code === 'ArrowDown') {
          e.preventDefault()
        }
        // 左右移动关闭操作面板
        if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {
          operationComponent.value = ''
        }
      } else {
        // 同步滚动高度
        unifiedScrollTop()
      }
      // 发送
    } else if (isEnter && !e.isComposing) {
      e.preventDefault()
      if (!operationShow.value) {
        send()
      }
    }
  })
  // 文本域自动获取焦点
  textareaFocus()
})
</script>

<template>
  <div class="post-text">
    <Transition name="more-operation" mode="out-in">
      <div v-if="operationShow" class="more-operation">
        <component :is="operationComponent" :word="searchWord" @select="select" @close="closeMoreOperation"></component>
      </div>
    </Transition>
    <div class="post-text-body">
      <div class="post-text-content">
        <div class="post-text-textarea-body">
          <div ref="textViewRef" class="post-text-textarea">
            <div
              v-html="textHandle(item, index, { showCommand: true, emojiText: true })"
              class="text-row"
              v-for="(item, index) in textContent"
              :key="index"></div>
          </div>
          <textarea
            ref="textareaRef"
            :value="messageBox.content"
            class="post-text-textarea"
            :rows="rows"
            :disabled="isProhibition"
            :placeholder="isProhibition ? '禁言中...' : `请输入...`"
            @input="textareaInput"
            @contextmenu="handleContextmenu"
            @scroll="unifiedScrollTop($event.target.scrollTop)"></textarea>
          <div v-if="menuVisible" class="post-text-contextmenu" :style="menuPosition">
            <div class="menu-item" @click="handlePaste">
              <el-icon size="16"><Connection></Connection></el-icon>
              粘贴
            </div>
          </div>
        </div>
        <div class="input-footer">
          <div class="input-tool">
            <template v-if="!disabledTool">
              <div class="quick_instructions" @click="handleToolClick('@')">@</div>
              <div style="margin-right: 5px; height: 25px; width: 25px">
                <Emoji
                  :emoji-data="emojiData"
                  fixPos="upright"
                  size="mid"
                  ref="emojiRef"
                  recent
                  :customSize="{ 'V3Emoji-itemSize': '25px' }"
                  @clickEmoji="clickEmoji">
                  <SvgIcon name="emoji-icon" size="19" color="var(--elp-text-color-secondary)"></SvgIcon>
                </Emoji>
              </div>
            </template>
            <el-dropdown
              split-button
              type="primary"
              placement="top-start"
              trigger="click"
              size="small"
              @command="changeTheSendingMethod"
              @click="send">
              发 送
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item command="1" :class="Number(appStore.sendShortcutKey) === 1 ? 'active' : ''">
                    Enter键发送 Ctrl+Enter键换行
                  </el-dropdown-item>
                  <el-dropdown-item command="2" :class="Number(appStore.sendShortcutKey) === 2 ? 'active' : ''">
                    Enter键换行 Ctrl+Enter键发送
                  </el-dropdown-item>
                  <el-dropdown-item command="3" :class="Number(appStore.sendShortcutKey) === 3 ? 'active' : ''">
                    Enter键换行 Alt+Enter键发送
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </div>
        </div>
      </div>
      <div v-if="files.length" class="post-text-files">
        <TextFileCard
          v-for="item in files"
          :key="`${item.name}_${item.lastModified}`"
          :file="item"
          delete
          @deleteFile="deleteFile"></TextFileCard>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.post-text {
  position: relative;
  width: 100%;
  border-radius: var(--elp-border-radius-8);
  border: var(--elp-border);
  background-color: var(--elp-bg-color);
  z-index: 2;
  .more-operation {
    position: absolute;
    top: -351px;
    left: 20px;
    background-color: var(--elp-bg-color);
    height: 350px;
    padding: 10px;
    overflow: hidden;
    border-radius: 8px;
    transition: all 0.3s;
    box-shadow: var(--elp-box-shadow-light);
    &-enter-from,
    &-leave-to {
      height: 0;
      padding: 0;
      top: -6px;
    }
  }
  .post-text-body {
    padding: 12px;
    .post-text-content {
      @include use-flex(center, normal, column);
      .post-text-textarea-body {
        @include use-flex(flex-start);
        position: relative;
        width: 100%;
        padding-right: 5px;
        .post-text-textarea {
          font-size: 14px;
          min-height: 76px;
          word-break: break-all;
          font-family:
            -apple-system,
            BlinkMacSystemFont,
            Segoe UI,
            Helvetica,
            Arial,
            sans-serif,
            'Apple Color Emoji',
            'Segoe UI Emoji';
        }
        div.post-text-textarea {
          position: relative;
          max-height: 95px;
          overflow-y: auto;
          z-index: 1;
          background-color: var(--elp-bg-color);
          .text-row {
            white-space: pre-wrap;
            min-height: 19px;
            overflow: hidden;
          }
        }
        textarea.post-text-textarea {
          width: calc(100% - 5px);
          border: none;
          resize: none;
          padding: 0;
          position: absolute;
          left: 0;
          z-index: 99;
          outline: none;
          color: transparent;
          caret-color: var(--elp-text-color-primary);
          background-color: transparent;
          &::placeholder {
            color: var(--elp-text-color-placeholder);
          }
          &::-webkit-scrollbar {
            width: 6px;
          }
          &::-webkit-scrollbar-thumb {
            width: 6px;
            border-radius: 6px;
            background-color: var(--elp-scrollbar-bg-color, var(--elp-text-color-secondary));
          }
        }
      }
      .input-footer {
        @include use-flex(center, space-between);
        width: 100%;
        .input-tool {
          flex: 1;
          min-width: 150px;
          @include use-flex(center, flex-end);
          .quick_instructions {
            font-size: 17px;
            color: var(--elp-text-color-secondary);
            margin-right: 5px;
            cursor: pointer;
          }
          .post-text-send {
            cursor: pointer;
          }
          .elp-button--small {
            border-radius: var(--elp-border-radius-base);
          }
        }
      }

      .post-text-contextmenu {
        position: absolute;
        font-size: 12px;
        padding: 5px;
        width: 100px;
        border-radius: 5px;
        background-color: #fff;
        box-shadow: var(--elp-box-shadow);
        z-index: 9999;
        .menu-item {
          @include use-flex(center);
          cursor: pointer;
          padding: 5px 10px;
          border-radius: 5px;
          &:hover {
            background-color: rgba(212, 216, 224, 0.5);
          }
          .elp-icon {
            color: #999;
            margin-right: 5px;
          }
        }
      }
    }
    .post-text-files {
      @include use-flex();
      flex-wrap: wrap;
      max-height: 120px;
      overflow-y: auto;
      padding-top: 10px;
    }
  }
}
:deep(.active) {
  background-color: var(--elp-dropdown-menuItem-hover-fill);
  color: var(--elp-dropdown-menuItem-hover-color);
}
:deep(.V3Emoji-module__emoji-item___aCxWs),
:deep(.V3Emoji-module__emoji-container-open-btn___Ru3N1) {
  @include use-flex(center, center);
}
</style>
